Example of conversion between struct and []byte in golang

  • 2020-06-19 10:27:57
  • OfStack

During network transport, socket often receives data, gets its header, and then does various business processes. There are many ways to parse headers. The most efficient way to parse headers is to cast the data header portion directly into the corresponding header structure. This practice is very common in C/C++. And golang can do the same thing. In applications like this, the parsing method corresponding to direct type conversion to get the message is actually relatively efficient.

The conversion method of struct and []byte in golang, in fact, is to use the unsafe package in golang plus type conversion, constraint: struct cannot have pointer type.

1. struct is converted to []byte by the following conversion methods:


import (
  "fmt"
  "unsafe"
)
type TestStructTobytes struct {
  data int64
}
type SliceMock struct {
  addr uintptr
  len int
  cap int
}

func main() {

  var testStruct = &TestStructTobytes{100}
  Len := unsafe.Sizeof(*testStruct)
  testBytes := &SliceMock{
    addr: uintptr(unsafe.Pointer(testStruct)),
    cap: int(Len),
    len: int(Len),
  }
  data := *(*[]byte)(unsafe.Pointer(testBytes))
  fmt.Println("[]byte is : ", data)
} 

Operation results:

[

[]byte is : [100 0 0 0 0 0 0 0]

]

Because the underlying data structure of []byte is:


struct { 
 addr uintptr 
 len int 
 cap int 
 } 

Where addr is the address of the value, len is the length of the local value and cap is the capacity of the value.

To do this, you need to define an struct to []byte underlying structure 1 (SliceMock in the example), then assign the address of the structure to addr and the size of the structure to len and cap. Finally, it is converted to the []byte type.

2. To convert []byte to struct, the conversion method is as follows:


import (
  "fmt"
  "unsafe"
)
type TestStructTobytes struct {
  data int64
}
type SliceMock struct {
  addr uintptr
  len int
  cap int
}

func main() {

  var testStruct = &TestStructTobytes{100}
  Len := unsafe.Sizeof(*testStruct)
  testBytes := &SliceMock{
    addr: uintptr(unsafe.Pointer(testStruct)),
    cap: int(Len),
    len: int(Len),
  }
  data := *(*[]byte)(unsafe.Pointer(testBytes))
  fmt.Println("[]byte is : ", data)
  var ptestStruct *TestStructTobytes = *(**TestStructTobytes)(unsafe.Pointer(&data))
  fmt.Println("ptestStruct.data is : ", ptestStruct.data)
} 

Operation results:

[

[]byte is : [100 0 0 0 0 0 0 0]
ptestStruct.data is : 100

]

The snippet to convert []byte to struct from the above example is:


var ptestStruct *TestStructTobytes = *(**TestStructTobytes)(unsafe.Pointer(&data))

Analysis:

Since in golang the compiler does not treat []byte as a pointer, it USES its address for translation, since the underlying []byte stores the address pointing to the data. Using the address of []byte requires the use of a double-layer pointer conversion, and then pointing to its contents, resulting in the conversion of the corresponding pointer to struct.


Related articles: